package NetWork;

import java.net.InetAddress;
import java.net.Socket;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Collection;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Vector;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;
import java.io.DataOutputStream;
import java.io.DataInputStream;

import javax.sql.rowset.CachedRowSet;

import NetWork.NetWorkDelegate;
import Global.DDBQuery;
import Global.TransObject;
import Global.ContentType;

public class SocketConnector extends Thread
{
	public Socket				socket;
	public String				hostString;
	public int					portNum;
	private boolean				keepAlive;
	private ObjectOutputStream	objectOutStream;
	private ObjectInputStream	objectInStream;
	private DataOutputStream	dataOutStream;
	private DataInputStream		dataInStream;
	public Queue<TransObject>	stringQueue;
	public Queue<TransObject>	objectQueue;
	public Queue<TransObject>	queryQueue;
	public Queue<TransObject>	resQueue;
	public StringScheduler		stringScheduler;
	public ObjectScheduler		objectScheduler;
	public QueryScheduler		queryScheduler;
	public ResultSetScheduler	resultSetScheduler;
	public static long			lengthOfReceivedBytes	= 0;
	public static long			lengthOfSentBytes		= 0;

	public boolean				isAbleToSendFile;

	public SocketConnector(String host, int port)
	{
		try
		{
			socket = new Socket(host, port);
			hostString = host;
			portNum = port;
			keepAlive = true;

			dataOutStream = new DataOutputStream(socket.getOutputStream());
			dataInStream = new DataInputStream(socket.getInputStream());
			objectOutStream = new ObjectOutputStream(socket.getOutputStream());
			objectInStream = new ObjectInputStream(socket.getInputStream());

			stringQueue = new LinkedList<TransObject>();
			objectQueue = new LinkedList<TransObject>();
			queryQueue = new LinkedList<TransObject>();
			resQueue = new LinkedList<TransObject>();
		}
		catch (Exception e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public SocketConnector(Socket s)
	{
		try
		{
			socket = s;
			hostString = s.getInetAddress().getHostAddress();
			portNum = s.getPort();
			keepAlive = true;

			dataOutStream = new DataOutputStream(socket.getOutputStream()); // file
			dataInStream = new DataInputStream(socket.getInputStream()); // file
			objectOutStream = new ObjectOutputStream(socket.getOutputStream()); // object
			objectInStream = new ObjectInputStream(socket.getInputStream()); // object

			stringQueue = new LinkedList<TransObject>();
			objectQueue = new LinkedList<TransObject>();
			queryQueue = new LinkedList<TransObject>();
			resQueue = new LinkedList<TransObject>();
		}
		catch (Exception e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/*
	 * public void sendFile(String fileName, int siteType) { if (fileName !=
	 * null) { try { File srcFile = new File(fileName); FileInputStream
	 * fileInputStream = new FileInputStream(srcFile); byte[] buffer = new
	 * byte[1024 4]; int readLen = 0; String fileInfoString =
	 * String.format("%d,%s", srcFile.length(), srcFile.getName());
	 * objectOutStream.writeObject(new
	 * TransObject(ContentType.Type_File.ordinal(), siteType, fileInfoString));
	 * isAbleToSendFile = false; while ((readLen = fileInputStream.read(buffer))
	 * > 0) { dataOutStream.write(buffer, 0, readLen); }
	 * System.out.printf("send to %s-(%s)\n", hostString, fileName);
	 * fileInputStream.close(); dataOutStream.flush(); } catch
	 * (FileNotFoundException e) { // TODO Auto-generated catch block
	 * System.out.println(fileName + " doesn't exist"); e.printStackTrace(); }
	 * catch (IOException e) { // TODO Auto-generated catch block
	 * e.printStackTrace(); } } }
	 * 
	 * public void receiveFile(String destPath, int size) { if (destPath ==
	 * null) return; try { File dest = new File(destPath); if
	 * (!dest.getParentFile().exists()) { dest.getParentFile().mkdirs(); }
	 * 
	 * int receivedBytes = 0; FileOutputStream fileOutStream = new
	 * FileOutputStream(dest); byte buffer[] = new byte[1024 4]; int readLen =
	 * 0; while ((readLen = dataInStream.read(buffer)) > 0) { receivedBytes +=
	 * readLen; fileOutStream.write(buffer, 0, readLen); if (receivedBytes ==
	 * size) break; } fileOutStream.close(); System.out.println("receive File: "
	 * + destPath); } catch (FileNotFoundException e) { e.printStackTrace(); }
	 * catch (IOException e) { e.printStackTrace(); } }
	 */

	public void sendString(String str, int siteType)
	{
		sendObject(new TransObject(ContentType.Type_String.ordinal(), siteType, String.format("%s\n", str)));
	}

	public void sendQuery(Object obj, int siteType)
	{
		sendObject(new TransObject(ContentType.Type_SqlString.ordinal(), siteType, obj));
	}

	public void sendQueryRes(Object obj, int siteType)
	{
		sendObject(new TransObject(ContentType.Type_queryResult.ordinal(), siteType, obj));
	}

	public void sendImportData(Vector<String> v, int siteType)
	{
		sendObject(new TransObject(ContentType.Type_ImportData.ordinal(), siteType, v));
	}

	private void sendObject(Object obj)
	{
		try
		{
			objectOutStream.writeObject(obj);
		}
		catch (IOException e)
		{
			e.printStackTrace();
		}
	}

	public void run()
	{
		while (keepAlive)
		{
			try
			{
				TransObject object = (TransObject) objectInStream.readObject();
				if (object.type == ContentType.Type_String.ordinal())
				{
					stringQueue.add(object);
					if (stringScheduler == null)
					{
						stringScheduler = new StringScheduler(hostString, portNum, stringQueue);
						stringScheduler.start();
					}
				}
				else if (object.type == ContentType.Type_SqlString.ordinal())
				{
					// System.out.printf("query=%s\n", ((DDBQuery)
					// object.obj).query);
					queryQueue.add(object);
					if (queryScheduler == null)
					{
						queryScheduler = new QueryScheduler(queryQueue);
						queryScheduler.start();
					}
				}
				else if (object.type == ContentType.Type_queryResult.ordinal())
				{
					resQueue.add(object);
					if (resultSetScheduler == null)
					{
						resultSetScheduler = new ResultSetScheduler(hostString, portNum, resQueue);
						resultSetScheduler.start();
					}
				}
				else if (object.type == ContentType.Type_ImportData.ordinal())
				{
					Vector<NetWorkDelegate> vector = TransmissionUtil.delegateVector;
					for (NetWorkDelegate d : vector)
					{
						d.didReceiveImportData(object.siteType, hostString, portNum, (Vector<String>) object.obj);
					}
				}
			}
			catch (Exception e)
			{
				// TODO Auto-generated catch block
				keepAlive = false;
				e.printStackTrace();
			}
		}
	}

	public static long sizeOfResultSet(CachedRowSet crs)
	{
		long size = 0;
		long rowSize = 0;
		int cnt = 100;
		try
		{
			for (int j = 0; j < cnt; j++)
			{
				crs.next();
				for (int i = 1; i <= crs.getMetaData().getColumnCount(); i++)
				{
					if (crs.getMetaData().getColumnType(i) == Types.INTEGER)
						rowSize += Integer.SIZE;
					else
						rowSize += crs.getString(i).length();
				}
			}
			size = rowSize * crs.size()/cnt;
		}
		catch (SQLException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return size;
	}

	public void closeConnection()
	{
		keepAlive = false;
		try
		{
			dataInStream.close();
			dataOutStream.close();
			objectInStream.close();
			objectOutStream.close();
		}
		catch (IOException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}